Skip to content

fix(desktop): local-first project icons - #5845

Merged
Kitenite merged 1 commit into
mainfrom
triage/issue-5843-29860767946
Jul 28, 2026
Merged

Kitenite merged 1 commit into
mainfrom
triage/issue-5843-29860767946

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The v2 project icon picker (IconUploadField) called the cloud v2Project.uploadIcon/resetIconToGitHub/removeIcon mutations, which are keyed by v2Projects.id. But v2 projects are host-local (useHostProjects fan-out over client.project.list) — their id has no cloud v2Projects row except for legacy cloud-created projects. So setting an icon failed with "Project not found" on every local-first project (the common case), and even on the projects where it "worked" the uploaded icon was never displayed (the v2 UI derived the icon from repoOwner). That's the #5843 regression.

CDP-confirmed before the fix: upload → Project not found on a local-first project; success only on a legacy cloud-linked project.

Fix — move icons to the local-first model

Project icons now work the same way name, branch prefix, and worktree location already do: host-service local storage.

Host-service

  • Add an icon column to the local projects table (migration 0012_dear_morph.sql, generated via drizzle-kit).
  • New project.setIcon mutation (mirrors setBranchPrefix), writing through updateLocalProject so it bumps updatedAt and broadcasts project:changed. Input validated as an image data-URI, capped at 256KB.
  • icon threaded through project.list/get, toProjectSnapshot, and the ProjectSnapshot event type.

Renderer

  • useHostProjects merge layer carries icon (local-host-wins).
  • V2ProjectSettings resolves the icon as custom → GitHub avatar → placeholder, so an uploaded icon shows immediately in both the header and the picker.
  • IconUploadField rewritten to call the host client project.setIcon, downscale the chosen image to a 128×128 PNG data-URI client-side (no cloud blob), and offer Upload image… / Reset to default.

Cloud teardown

  • Deleted the orphaned cloud uploadIcon/resetIconToGitHub/removeIcon procs + their tests. The renderer now has zero v2Project.* calls. (The v2Projects.iconUrl column is left in place; dropping it belongs to the broader teardown — see plans/remove-cloud-project-model.md.)

Verification

  • typecheck clean (host-service, trpc, desktop); biome clean.
  • Tests: host-service integration 9 pass (2 new setIcon tests, mutation-verified to fail without the fix); trpc v2-project 18 pass; V2ProjectSettings 1 pass.
  • CDP end-to-end on the local-first superset project that returned "Project not found" before: upload → icon persists as data:image/png and displays; Reset to default → reverts to the GitHub avatar. No more "Project not found".

Context

This supersedes the original cloud-based wiring on this branch. It's Phase A of a broader effort to stop the local-first desktop app from querying the cloud for app data (chat, tasks, automations, secrets) — scoped in plans/remove-cloud-project-model.md.

Closes #5843

@Kitenite Kitenite changed the title fix(desktop): restore project icon picker in v2 project settings (#5843) fix(desktop): make v2 project icons local-first Jul 22, 2026
@Kitenite
Kitenite marked this pull request as ready for review July 22, 2026 05:28
@github-actions

github-actions Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor Author

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) ✅ View Branch
Vercel API (Vercel) ✅ Open Preview
Vercel Web (Vercel) ✅ Open Preview
Vercel Marketing (Vercel) ✅ Open Preview
Vercel Admin (Vercel) ✅ Open Preview
Vercel Docs (Vercel) ✅ Open Preview

Preview updates automatically with new commits

@ghost

ghost commented Jul 28, 2026 •

Copy link
Copy Markdown

@Kitenite
Kitenite force-pushed the triage/issue-5843-29860767946 branch 2 times, most recently from 720f486 to 14f790a Compare July 28, 2026 03:12
@Kitenite Kitenite changed the title fix(desktop): make v2 project icons local-first fix(desktop): local-first project icons Jul 28, 2026
@Kitenite
Kitenite force-pushed the triage/issue-5843-29860767946 branch from 14f790a to ca12aa8 Compare July 28, 2026 03:17
Project icons were a cloud-only feature (v2Project.uploadIcon/resetIconToGitHub/
removeIcon keyed by v2Projects.id), but v2 projects are host-local — their id
has no cloud row except for legacy cloud-created projects. So setting an icon
failed with "Project not found" on every local-first project (#5843), and the
uploaded icon was never displayed (the UI derived icons from repoOwner).

Move project icons fully into the host-service local model and remove the dead
cloud machinery.

Host-service (local storage):
- Add an `icon` column to the host `projects` table (migration 0012) and a
  `project.setIcon` mutation that writes via updateLocalProject (bumps
  updatedAt + broadcasts project:changed). Thread `icon` through
  project.list/get, ProjectSnapshot, and the useHostProjects merge layer.

Desktop renderer:
- Single resolveProjectIconUrl helper (custom icon -> GitHub avatar -> none),
  used by every project-icon surface: settings, dashboard sidebar, project
  list, task filter, recent projects, new-workspace picker, and the
  v2-workspaces list/header (V2WorkspaceProjectIcon now takes a resolved
  iconUrl). No site derives the URL from repoOwner anymore.
- IconUploadField calls the host client, downscales the chosen image to a
  128x128 PNG data-URI client-side (no cloud blob), and offers Upload / Reset.

Mobile:
- useHostProjects honors the host `icon` field for parity.

Removals (cloud icon model is now unused):
- Delete v2Project.uploadIcon/resetIconToGitHub/removeIcon and the dead
  GitHub-avatar icon hydration in create/linkRepoCloneUrl.
- Delete the orphaned fetchAndStoreGitHubAvatar helper.
- Drop the dead projectGithubOwner/githubOwner plumbing that only fed the icon.

The cloud v2Projects.iconUrl column is left in place; dropping it needs the
broader Electric/mobile teardown (see plans/remove-cloud-project-model.md).

Verified end-to-end via CDP: setting an icon in settings updates the sidebar
and workspace list live; reset reverts to the GitHub avatar; no
"Project not found".

Closes #5843
@Kitenite
Kitenite force-pushed the triage/issue-5843-29860767946 branch from ca12aa8 to d03dfe4 Compare July 28, 2026 10:27
@Kitenite
Kitenite merged commit 05e7ba3 into main Jul 28, 2026
16 checks passed
@Kitenite
Kitenite deleted the triage/issue-5843-29860767946 branch July 28, 2026 10:31
Kitenite added a commit that referenced this pull request Aug 7, 2026
… v1 import carry-over (#6249)

* feat(desktop,host-service): v2 project colors — icon+color picker, v1 import carry-over

- host-service: add projects.color column (migration 0015), thread it
  through ProjectSnapshot / project.list / project.get, and add a
  project.setColor mutation mirroring setIcon
- v1→v2 import: carry the v1 accent color onto the imported v2 project
  (best-effort, both the auto-migrator and the wizard path via
  importV1Project); 'default' sentinel stays unset
- v2 settings: extend IconUploadField into a simple icon+color picker —
  color swatches (ColorSelector) plus a lucide glyph grid baked into the
  existing setIcon data-URI path, alongside upload/reset
- render the accent color on the v2 ProjectThumbnail (tinted border and
  letter fallback, matching v1) in project settings, the settings
  project list, and the dashboard sidebar

The 'Remove icon' tRPC error (v2/project/removeIcon) was already fixed
on main by #5845; no call sites remain and reset goes through
project.setIcon(null).

* feat(desktop,host-service): allow removing the project icon with a v1-style letter fallback

- setIcon accepts a 'none' sentinel: explicitly no icon, suppressing the
  GitHub avatar fallback so every surface renders the first-letter tile
  (tinted by the project color) via resolveProjectIconUrl — v1's
  hide_image equivalent
- picker gains 'Remove icon' (when any icon shows) alongside 'Reset to
  default'; the trigger thumbnail now renders ProjectThumbnail, so an
  icon-less project shows its letter tile instead of a placeholder
- v1→v2 import carries hide_image as the 'none' icon

* fix(desktop): gate glyph re-bake on persisted color and disable swatches while pending

Review follow-ups (Greptile/CodeRabbit/cubic):
- setIcon/setColor return success; a rejected color no longer re-bakes the
  session glyph into a color the project never stored, and a failed setIcon
  no longer marks the glyph as session-active
- ColorSelector gains a disabled prop; the picker disables swatches during
  mutations so rapid clicks can't land out of order
- ProjectThumbnail accepts only exact #rrggbb before tinting (hexToRgba
  slices fixed offsets; malformed values now render as unset)

This branch was previously deployed

1 inactive deployment
preview — d03dfe44 Deployed Jul 28, 2026 by Kitenite via Deploy Web #6903
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Project icons are missing in 1.16

1 participant